package org.cable.shop.service.impl;

import com.sun.org.apache.regexp.internal.RE;
import groovyjarjarpicocli.CommandLine.IFactory;
import io.netty.handler.codec.compression.Bzip2Decoder;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.cable.common.constants.TimeConstant;
import org.cable.common.enums.BillTypeEnum;
import org.cable.common.enums.BizCodeEnum;
import org.cable.common.enums.EventMessageType;
import org.cable.common.enums.ProductOrderPayTypeEnum;
import org.cable.common.enums.ProductOrderStateEnum;
import org.cable.common.exception.BizException;
import org.cable.common.interceptor.LoginInterceptor;
import org.cable.common.model.EventMessage;
import org.cable.common.model.LoginUser;
import org.cable.common.util.CommonUtil;
import org.cable.common.util.JsonData;
import org.cable.common.util.JsonUtil;
import org.cable.shop.component.PayFactory;
import org.cable.shop.config.RabbitMQConfig;
import org.cable.shop.controller.request.ConfirmOrderRequest;
import org.cable.shop.manager.ProductManager;
import org.cable.shop.manager.ProductOrderManager;
import org.cable.shop.model.ProductDO;
import org.cable.shop.model.ProductOrderDO;
import org.cable.shop.service.ProductOrderService;
import org.cable.shop.vo.PayInfoVO;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Map;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author hsj
 * @date 2023/11/7 10:41
 */
@Service
@Slf4j
public class ProductOrderServiceImpl implements ProductOrderService {

	@Autowired
	private ProductOrderManager productOrderManager;

	@Autowired
	private ProductManager productManager;

	@Autowired
	private RabbitTemplate rabbitTemplate;

	@Autowired
	private RabbitMQConfig rabbitMQConfig;

	@Autowired
	private PayFactory payFactory;

	@Autowired
	private RedisTemplate<Object,Object> redisTemplate;

	@Override
	public Map<String, Object> page(int page, int size, String state) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		Map<String, Object> result = productOrderManager.page(page, size, accountNo, state);
		return result;
	}

	@Override
	public String queryProductOrderState(String orderTraderNo) {
		Long accountNo = LoginInterceptor.threadLocal.get().getAccountNo();
		ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(orderTraderNo, accountNo);
		if (null == productOrderDO) {
			return "";
		}
		return productOrderDO.getState();
	}

	@Override
	@Transactional
	public JsonData confirmOrder(ConfirmOrderRequest orderRequest) {

		LoginUser loginUser = LoginInterceptor.threadLocal.get();
		String outTradeNo = CommonUtil.getStringNumRandom(32);

		ProductDO productDO = productManager.findDetailById(orderRequest.getProductId());
		//验证价格
		this.checkPrice(productDO, orderRequest);

		//创建订单
		ProductOrderDO productOrderDO = this.saveProductOrder(orderRequest, loginUser, outTradeNo, productDO);

		//创建支付信息
		PayInfoVO payInfoVO = PayInfoVO.builder().accountNo(loginUser.getAccountNo())
		                               .outTradeNo(outTradeNo)
		                               .clientType(orderRequest.getClientType())
		                               .payType(orderRequest.getPayType())
		                               .title(productDO.getTitle())
		                               .description(productDO.getDetail())
		                               .orderPayTimeoutMills(TimeConstant.ORDER_PAY_TIMEOUT_MILLS)
		                               .payFee(orderRequest.getPayAmount()).build();

		// 发送延迟消息
		EventMessage eventMessage = EventMessage.builder()
		                                        .eventMessageType(EventMessageType.PRODUCT_ORDER_NEW.name())
		                                        .accountNo(loginUser.getAccountNo())
		                                        .bizId(outTradeNo)
		                                        .build();

		rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
		                              rabbitMQConfig.getOrderCloseDelayRoutingKey(),
		                              eventMessage);

		//对接支付信息
		String codeUrl = payFactory.pay(payInfoVO);
		if (StringUtils.isNotBlank(codeUrl)){
			Map<String,String> resultMap = new HashMap<>(2);
			resultMap.put("code_url",codeUrl);
			resultMap.put("out_trade_no",payInfoVO.getOutTradeNo());
			return JsonData.buildSuccess(resultMap);
		}
		return JsonData.buildSuccess(BizCodeEnum.PAY_ORDER_FAIL);
	}

	private ProductOrderDO saveProductOrder(ConfirmOrderRequest orderRequest, LoginUser loginUser, String outTradeNo,
		ProductDO productDO) {

		ProductOrderDO productOrderDO = new ProductOrderDO();

		//设置用户信息
		productOrderDO.setAccountNo(loginUser.getAccountNo());
		productOrderDO.setNickname(loginUser.getUsername());
		productOrderDO.setProductId(productDO.getId());

		//设置商品信息
		productOrderDO.setProductTitle(productDO.getTitle());
		productOrderDO.setProductSnapshot(JsonUtil.obj2Json(productDO));
		productOrderDO.setProductAmount(productDO.getAmount());

		//设置订单信息
		productOrderDO.setBuyNum(orderRequest.getBuyNum());
		productOrderDO.setOutTradeNo(outTradeNo);
		productOrderDO.setCreateTime(new Date());
		productOrderDO.setDel(0);

		//设置发票信息
		productOrderDO.setBillType(BillTypeEnum.valueOf(orderRequest.getBillType()).name());
		productOrderDO.setBillHeader(orderRequest.getBillHeader());
		productOrderDO.setBillReceiverPhone(orderRequest.getBillReceiverPhone());
		productOrderDO.setBillReceiverEmail(orderRequest.getBillReceiverEmail());
		productOrderDO.setBillContent(orderRequest.getBillContent());

		//实际支付总价
		productOrderDO.setPayAmount(orderRequest.getPayAmount());
		//总价: 没使用优惠价
		productOrderDO.setTotalAmount(orderRequest.getTotalAmount());
		productOrderDO.setState(ProductOrderPayTypeEnum.valueOf(orderRequest.getPayType()).name());

		//插入数据库
		productOrderManager.add(productOrderDO);

		return productOrderDO;
	}

	private void checkPrice(ProductDO productDO, ConfirmOrderRequest orderRequest) {

		//后端计算价格
		BigDecimal bizTotal = BigDecimal.valueOf(orderRequest.getBuyNum()).multiply(productDO.getAmount());
		//前端传递的总价和后端价格是否一致
		if (bizTotal.compareTo(orderRequest.getPayAmount()) != 0) {
			log.error("验证价格失败:{}", orderRequest);
			throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
		}
	}

	/**
	 * 延迟消息的时间 需要⽐订单过期 时间⻓⼀点，这样就不存 在查询的时候，⽤户还能⽀付成功 查询订单是否存在，如果已经⽀付则正常结束 如果订单未⽀付，主动调⽤第三⽅⽀付平台查询订单状态 确认未⽀付，本地取消订单
	 * 如果第三⽅平台已经⽀付，主动的把订单状态改成已⽀付，造成该原因的情况可能是⽀付通道回调有问题，然后触发⽀付后的动作，如何触发？RPC还是？
	 */

	@Override
	public boolean closeProductOrder(EventMessage eventMessage) {
		String outTradeNo = eventMessage.getBizId();
		Long accountNo = eventMessage.getAccountNo();
		ProductOrderDO productOrderDO = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);
		if (null == productOrderDO) {
			//订单不存在
			log.warn("订单不存在");
			return true;
		}

		if (productOrderDO.getState().equals(ProductOrderStateEnum.PAY.name())) {
			log.info("直接确认消息,订单已经支付:{}", eventMessage);
			return true;
		}
		//未支付,需要向第三方支付平台查询状态
		if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())){
           //向第三方查询状态
			PayInfoVO payInfoVO = new PayInfoVO();
			payInfoVO.setPayType(productOrderDO.getPayType());
			payInfoVO.setOutTradeNo(outTradeNo);
			payInfoVO.setAccountNo(accountNo);

			//todo: 向第三方支付平台查询状态
			String payResult = "";
			if(StringUtils.isBlank(payResult)){
				//如果为空,则未支付成功,本地取消订单
				productOrderManager.updateOrderPayState(outTradeNo,accountNo,ProductOrderStateEnum.CANCEL.name(),ProductOrderStateEnum.NEW.name());
			    log.info("未支付成功,本地取消订单：{}",eventMessage);
			}else {
				//支付成功,主动把支付状态更新成支付
		       log.warn("支付成功,但是微信回调通知失败,需要排查问题:{}",eventMessage);
			   productOrderManager.updateOrderPayState(outTradeNo,accountNo,ProductOrderStateEnum.PAY.name(),ProductOrderStateEnum.NEW.name());

			   //todo:触发支付成功后的逻辑
			}
		}

		return true;
	}

	/**
	 * 处理微信回调通知
	 * @param payType
	 * @param paramMap
	 */
	@Override
	@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
	public JsonData processOrderCallBackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramMap) {

		//获取商户订单号
		String outTradeNo = paramMap.get("out_trade_no");
		//交易状态
		String tradeStatus = paramMap.get("trade_status");

		Long accountNo =Long.valueOf(paramMap.get("account_no"));

		ProductOrderDO productOrderDo = productOrderManager.findByOutTradeNoAndAccountNo(outTradeNo, accountNo);

		Map<String,Object> content = new HashMap<>(4);
		content.put("outTradeNo",outTradeNo);
		content.put("buyNum",productOrderDo.getBuyNum());
		content.put("accountNo",accountNo);
		content.put("product",productOrderDo.getProductSnapshot());
		EventMessage eventMessage = EventMessage.builder().bizId(outTradeNo)
		                                 .accountNo(accountNo)
		                                 .messageId(outTradeNo)
		                                 .content(JsonUtil.obj2Json(content))
		                                 .eventMessageType(EventMessageType.PRODUCT_ORDER_PAY.name())
		                                 .build();
		if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALI_PAY.name())){
			//todo: 支付宝支付
		}else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT_PAY.name())){
			if ("SUCCESS".equalsIgnoreCase(tradeStatus)){

				//如果key不存在,则设置成功,返回true  防止重复推送
				Boolean absent = redisTemplate.opsForValue().setIfAbsent(outTradeNo, "OK", 3, TimeUnit.DAYS);
				if (absent){
					rabbitTemplate.convertAndSend(rabbitMQConfig.getOrderEventExchange(),
					                              rabbitMQConfig.getOrderUpdateTrafficRoutingKey(),
					                              eventMessage);
					return JsonData.buildSuccess();
				}
			}
		}

		return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
	}

	/**
	 * 处理订单相关消息
	 * @param eventMessage
	 */
	@Override
	public void handleProductOrderMessage(EventMessage eventMessage) {

		String messageType = eventMessage.getEventMessageType();

		try {

			if (EventMessageType.PRODUCT_ORDER_NEW.name().equalsIgnoreCase(messageType)){
				//关闭订单
				this.closeProductOrder(eventMessage);
			}else if (EventMessageType.PRODUCT_ORDER_PAY.name().equalsIgnoreCase(messageType)){
				//订单已经支付,更新订单状态
				String outTrade = eventMessage.getBizId();
				Long accountNo = eventMessage.getAccountNo();
				int rows = productOrderManager.updateOrderPayState(outTrade,
				                                                accountNo,
				                                                ProductOrderStateEnum.PAY.name(),
				                                                ProductOrderStateEnum.NEW.name());
				log.info("订单更新成功:rows = {},eventMessage={}",rows,eventMessage);
			}

		}catch (Exception e){
			log.error("订单消费者消费失败:{}",eventMessage);
			throw new BizException(BizCodeEnum.MQ_CONSUMER_EXCEPTION);
		}


	}

}
